#include <iostream>
#include <windows.h>
#include <windowsx.h>

#include "win32WindowImpl.h"
#include "neiderra/render/iRenderer.h"
#include "neiderra/gui/iGraphicsSubsystem.h"

using namespace neiderra::core;
using namespace neiderra::rendering;
using namespace std;

LRESULT CALLBACK win32WindowImpl::MsgRouter(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
	//std::clog << "Win32WindowImpl::MsgRouter\n";
	win32WindowImpl *wndImpl = 0;

	if(uMsg == WM_NCCREATE)
    {
        // retrieve Window instance from window creation data and associate
        wndImpl = reinterpret_cast<win32WindowImpl *>(((LPCREATESTRUCT)lParam)->lpCreateParams);
        ::SetWindowLong(hWnd, GWL_USERDATA, reinterpret_cast<long>(wndImpl));

      // save window handle
        if (wndImpl) wndImpl->setHWND(hWnd);

		//std::cout << "Window has been created\n";
    }
    else
        // retrieve associated Window instance
        wndImpl = reinterpret_cast<win32WindowImpl*>(::GetWindowLong(hWnd, GWL_USERDATA));

    // call the windows message handler
    if (wndImpl) return wndImpl->WndProc(uMsg, wParam, lParam);
	else return 0;
}

void win32WindowImpl::swapBuffers(){
	::SwapBuffers( hDC_ );
}

#include "neiderra/gui/Events.h"
#include "neiderra/gui/EventManager.h"
#include "neiderra/core/VirtualKeyCodes.h"

using namespace neiderra::gui::events;

LRESULT win32WindowImpl::WndProc(UINT message, WPARAM wparam, LPARAM lparam){
	iGUIEvent* event;
	GUIMouseEvent* mouse_event;
	GUIKeyEvent* keyEvent;
	//PAINTSTRUCT ps;
    switch(message){
	case WM_LBUTTONDOWN:
			mouse_event = new GUIMouseEvent;
			mouse_event->button_num = GUIMouseEvent::mb_left;    // left
			mouse_event->type = GUIMouseEvent::mouse_down;
			mouse_event->pwin = container_;
			mouse_event->mx = GET_X_LPARAM(lparam);
			mouse_event->my = GET_Y_LPARAM(lparam);
			//cout << "container title: " << container_->getTitle() << endl;
			core->getGraphicsSubsystem()->getEventManager()->pushEvent(mouse_event);
        return 0;
	case WM_MOUSEMOVE:
			mouse_event = new GUIMouseEvent;
			mouse_event->type = GUIMouseEvent::mouse_move;
			mouse_event->mx = GET_X_LPARAM(lparam);
			mouse_event->my = GET_Y_LPARAM(lparam);
			mouse_event->pwin = container_;
			core->getGraphicsSubsystem()->getEventManager()->pushEvent(mouse_event);
		break;
    case WM_RBUTTONDOWN:
			mouse_event = new GUIMouseEvent;
			mouse_event->button_num = GUIMouseEvent::mb_right;    
			mouse_event->type = GUIMouseEvent::mouse_down;
			mouse_event->pwin = container_;
			mouse_event->mx = GET_X_LPARAM(lparam);
			mouse_event->my = GET_Y_LPARAM(lparam);
			core->getGraphicsSubsystem()->getEventManager()->pushEvent(mouse_event);
        return 0;
	case WM_LBUTTONUP:
			mouse_event = new GUIMouseEvent;
			mouse_event->button_num = GUIMouseEvent::mb_left;    
			mouse_event->type = GUIMouseEvent::mouse_up;
			mouse_event->pwin = container_;
			mouse_event->mx = GET_X_LPARAM(lparam);
			mouse_event->my = GET_Y_LPARAM(lparam);
			core->getGraphicsSubsystem()->getEventManager()->pushEvent(mouse_event);
		break;
	case WM_KEYDOWN:
		keyEvent = new GUIKeyEvent();
		
		switch(wparam)
		{
		case VK_LEFT:
			keyEvent->keycode = vk_left;
			break;
		case VK_RIGHT:
			keyEvent->keycode = vk_right;
			break;
		case VK_UP:
			keyEvent->keycode = vk_up;
			break;
		case VK_DOWN:
			keyEvent->keycode = vk_down;
			break;
		case VK_SPACE:
			keyEvent->keycode = vk_space;
			break;
		case VK_RETURN:
			keyEvent->keycode = vk_enter;
			break;
		case VK_ESCAPE:
			keyEvent->keycode = vk_esc;
			break;
		case VK_NUMPAD7:
			keyEvent->keycode = vk_numpad7;
			break;
		case VK_NUMPAD9:
			keyEvent->keycode = vk_numpad9;
			break;
		case VK_NUMPAD8:
			keyEvent->keycode = vk_numpad8;
			break;
		case VK_NUMPAD5:
			keyEvent->keycode = vk_numpad5;
			break;
		case VK_NUMPAD4:
			keyEvent->keycode = vk_numpad4;
			break;
		case VK_NUMPAD6:
			keyEvent->keycode = vk_numpad6;
			break;
		case VK_F5:
			keyEvent->keycode = vk_f5;
			break;

		default:
            keyEvent->keycode = wparam;
		}
		
		keyEvent->pwin = container_;
		keyEvent->type = GUIKeyEvent::key_down;
		core->getGraphicsSubsystem()->getEventManager()->pushEvent(keyEvent);
		return 0;
	//case WM_KEYUP:
	//	switch(wparam)
	//	{
	//	default:
 //           keyEvent = new aKeyEvent();
	//		keyEvent->keycode = wparam;
	//		keyEvent->pwin = container_;
	//		keyEvent->type = key_up;
	//		core->getEventManager()->pushEvent(keyEvent);
	//	}
	//	return 0;
	case WM_DESTROY:
		// todo : GUIWindowEvent (type: closed)
		// fixme: don't shutdown, just say that window has been destroyed
		core->shutdown();
        PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProc(hWnd_, message, wparam, lparam);
}

win32WindowImpl::win32WindowImpl(Core* core, GUIWindow* container, const std::string& title, win32WndClass* wndClass): 
		GUIWindowImpl(core, container) 
	{
		wndClass_ = wndClass;

		// fixme: window title

		hWnd_ = CreateWindow(wndClass_->getName(), title.c_str(), WS_OVERLAPPEDWINDOW,	CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle( NULL ), this);

		//if( hWnd_ == NULL) std::clog<< "Can't create window\n";

		hDC_ = GetDC(hWnd_);
	}

win32WindowImpl::win32WindowImpl(Core* core, GUIWindow* container,
    const std::string& title,
    win32WndClass* wndClass,
    int x, int y, int width, int height,
    GUIWindow* parent): GUIWindowImpl(core, container)
{
    long id=0;
    if(parent) id = parent->getWindowImpl()->getID();

    wndClass_ = wndClass;
	const char* stitle = title.c_str();
    hWnd_ = CreateWindow( wndClass->getName(),   stitle, WS_OVERLAPPEDWINDOW,
        x, y, width, height, (HWND)id,
        0, GetModuleHandle( NULL ), this);

    hDC_ = GetDC(hWnd_);
}

long win32WindowImpl::getID()
{
    return (long)this->getHWND();
}

/*	virtual void setRenderer(aRenderer<Win32GLRenderImpl>* renderer){
		if (renderer) renderer->init(this);
	}
*/
bool win32WindowImpl::isAlive(){return (hWnd_ != NULL);}

HWND win32WindowImpl::getHWND(){ return hWnd_; }

void win32WindowImpl::setHWND(HWND hWnd){hWnd_ = hWnd;}

void win32WindowImpl::show(){
	::ShowWindow( hWnd_, SW_SHOW );
}

void win32WindowImpl::update(){
	::UpdateWindow( hWnd_ );
}

unsigned win32WindowImpl::getWidth()
{
	RECT r;
	GetClientRect(hWnd_, &r);
	return r.right-r.left;
}

unsigned win32WindowImpl::getHeight()
{
	RECT r;
	GetClientRect(hWnd_, &r);
	return r.bottom-r.top;
}

/*void Win32WindowImpl::setDimentions(unsigned w, unsigned h)
{
	width_ = w;
	height_ = h;
//	SetClientRect(hwnd_,

}*/

void win32WindowImpl::setRenderer(iRenderer* r)
{}

win32WindowImpl::~win32WindowImpl(){
	::DestroyWindow( hWnd_ );
	::ReleaseDC( hWnd_, hDC_ );
}

